﻿using System;
using System.Collections.Generic;
using System.Text;
using BlogHunter.Manager;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using BlogHunter.Util;
using System.Configuration;

namespace BlogHunter
{
    abstract class Blog
    {
        private string _inputBlogName;
        public string InputBlogName
        {
            get { return _inputBlogName; }
            set { _inputBlogName = value; }
        }

        private Uri _inputBlogHomePage;
        public Uri InputBlogHomePage
        {
            get { return _inputBlogHomePage; }
            set { _inputBlogHomePage = value; }
        }

        private Uri _blogHomePage;
        public Uri BlogHomePage
        {
            get { return _blogHomePage; }
            set { _blogHomePage = value; }
        }

        private Boolean _autoSplice;
        public Boolean AutoSplice
        {
            get { return _autoSplice; }
            set { _autoSplice = value; }
        }

        private string _spliceRule;
        public string SpliceRule
        {
            get { return _spliceRule; }
            set { _spliceRule = value; }
        }

        private string _outPath;
        public string OutPath
        {
            get { return _outPath; }
            set { _outPath = value; }
        }

        private string _xmlFilePath;
        public string XMLFilePath
        {
            get { return _xmlFilePath; }
            set { _xmlFilePath = value; }
        }

        private List<BlogInfo> _previousBlogInfoList = new List<BlogInfo>();
        public List<BlogInfo> PreviousBlogInfoList
        {
            get { return _previousBlogInfoList; }
            set { _previousBlogInfoList = value; }
        }

        private List<BlogInfo> _newBlogInfoList = new List<BlogInfo>();
        public List<BlogInfo> NewBlogInfoList
        {
            get { return _newBlogInfoList; }
            set { _newBlogInfoList = value; }
        }

        private DateTime _latestPublishDate;
        public DateTime LatestPublishDate
        {
            get { return _latestPublishDate; }
            set { _latestPublishDate = value; }
        }

        private Regex _blogInfoRegex;
        public Regex BlogInfoRegex
        {
            get { return _blogInfoRegex; }
            set { _blogInfoRegex = value; }
        }

        private int _linkRegexGroupNum;
        public int LinkRegexGroupNum
        {
            get { return _linkRegexGroupNum; }
            set { _linkRegexGroupNum = value; }
        }

        private int _titleRegexGroupNum;
        public int TitleRegexGroupNum
        {
            get { return _titleRegexGroupNum; }
            set { _titleRegexGroupNum = value; }
        }

        private int _datetimeRegexGroupNum;
        public int DatetimeRegexGroupNum
        {
            get { return _datetimeRegexGroupNum; }
            set { _datetimeRegexGroupNum = value; }
        }

        private string _encodingCharset;
        public string EncodingCharset
        {
            get { return _encodingCharset; }
            set { _encodingCharset = value; }
        }

        private Regex _nextPageRegex;
        public Regex NextPageRegex
        {
            get { return _nextPageRegex; }
            set { _nextPageRegex = value; }
        }

        private int _nextPageRegexGroupNum;
        public int NextPageRegexGroupNum
        {
            get { return _nextPageRegexGroupNum; }
            set { _nextPageRegexGroupNum = value; }
        }


        private Regex _chineseDateTimeRegex;
        public Regex ChineseDateTimeRegex
        {
            get { return _chineseDateTimeRegex; }
            set { _chineseDateTimeRegex = value; }
        }

        private int _chineseYearRegexGroupNum;
        public int ChineseYearRegexGroupNum
        {
            get { return _chineseYearRegexGroupNum; }
            set { _chineseYearRegexGroupNum = value; }
        }

        private int _chineseMonthRegexGroupNum;
        public int ChineseMonthRegexGroupNum
        {
            get { return _chineseMonthRegexGroupNum; }
            set { _chineseMonthRegexGroupNum = value; }
        }

        private int _chineseDateRegexGroupNum;
        public int ChineseDateRegexGroupNum
        {
            get { return _chineseDateRegexGroupNum; }
            set { _chineseDateRegexGroupNum = value; }
        }

        private string _replacePatternBeforeExtract;
        public string ReplacePatternBeforeExtract
        {
            get { return _replacePatternBeforeExtract; }
            set { _replacePatternBeforeExtract = value; }
        }

        private Dictionary<string, string> _postingParams;
        public Dictionary<string, string> PostingParams
        {
            get { return _postingParams; }
            set { _postingParams = value; }
        }

        private int _dateRegexGroupNum;
        public int DateRegexGroupNum
        {
            get { return _dateRegexGroupNum; }
            set { _dateRegexGroupNum = value; }
        }

        private int _timeRegexGroupNum;
        public int TimeRegexGroupNum
        {
            get { return _timeRegexGroupNum; }
            set { _timeRegexGroupNum = value; }
        }

        private int _articleCountPerPage;
        public int ArticleCountPerPage
        {
            get { return _articleCountPerPage; }
            set { _articleCountPerPage = value; }
        }

        private string _articleLinkSpliceRule;
        public string ArticleLinkSpliceRule
        {
            get { return _articleLinkSpliceRule; }
            set { _articleLinkSpliceRule = value; }
        }

        /// <summary>
        /// Auto Splice Baidu Blog Home Page URI, or utilize the input one.
        /// </summary>
        public void GenerateBlogHomePage()
        {
            if (AutoSplice && !String.IsNullOrEmpty(InputBlogName))
            {
                BlogHomePage = new Uri(SpliceRule.Replace("$BLOGNAME$", InputBlogName));
            }
            else
            {
                BlogHomePage = InputBlogHomePage;
            }
        }

        /// <summary>
        /// Generate XML File Path
        /// </summary>
        public void GenerateXMLFilePath()
        {
            if (OutPath.LastIndexOf("\\") < OutPath.Length - 1)
            {
                XMLFilePath = OutPath + "\\" + InputBlogName + ".xml";
            }
            else
            {
                XMLFilePath = OutPath + InputBlogName + ".xml";
            }
        }

        /// <summary>
        /// Get And Sort Previous BlogInfo List
        /// </summary>
        public void GetAndSortPreviousBlogInfoList()
        {
            if (System.IO.File.Exists(XMLFilePath))
            {
                StreamReader reader = new StreamReader(XMLFilePath, Encoding.GetEncoding(EncodingCharset));
                PreviousBlogInfoList = SerializerManager.Deserialize(typeof(List<BlogInfo>), reader.ReadToEnd()) as List<BlogInfo>;
                PreviousBlogInfoList.Sort(new DatetimeDESCComparision());

                reader.Close();
            }
        }

        /// <summary>
        /// Get Lastest Publish Datetime
        /// </summary>
        public void GetLastestPublishDatetime()
        {
            if (PreviousBlogInfoList != null && PreviousBlogInfoList.Count > 0)
            {
                LatestPublishDate = PreviousBlogInfoList[0].ArticleDateTime;
            }
        }

        /// <summary>
        /// Get All New BlogInfo List
        /// </summary>
        public virtual void GetAllNewBlogInfoList()
        {
            //Loop Each Page to Get BlogInfo List

            Uri currentUrl = BlogHomePage;
            do
            {
                currentUrl = GetNewBlogInfoList(currentUrl);
            } while (currentUrl != null);
        }

        /// <summary>
        /// Get Target HTML Content
        /// </summary>
        /// <param name="targetUri"></param>
        /// <param name="encodeType"></param>
        /// <returns></returns>
        public virtual string GetTargetHTMLContent(Uri targetUri, Encoding encodeType)
        {
            return HTMLContentManager.GetHTMLContent(targetUri, encodeType);
        }


        /// <summary>
        /// Get New BlogInfo List
        /// </summary>
        /// <param name="currentUri"></param>
        /// <returns></returns>
        public Uri GetNewBlogInfoList(Uri currentUri)
        {
            Uri result = null;

            bool needUpadteFlag = true;

            String inputString = GetTargetHTMLContent(currentUri, Encoding.GetEncoding(EncodingCharset));

            //Replace Pattern Before Extract, Eg. We should remove Sina Blog Picture HTML contents before Extracting.
            if (!String.IsNullOrEmpty(ReplacePatternBeforeExtract))
            {
                inputString = Regex.Replace(inputString, ReplacePatternBeforeExtract, string.Empty);
            }

            Regex myRegex = BlogInfoRegex;
            if (myRegex.IsMatch(inputString))
            {
                Match myMatch = myRegex.Match(inputString);
                while (myMatch.Success)
                {
                    BlogInfo newBlogInfo = new BlogInfo();

                    string link = Regex.Replace(myMatch.Groups[LinkRegexGroupNum].Value.Replace("\r\n", " ").Trim(), @"( )\1+", "$1", RegexOptions.None);
                    Console.WriteLine(link);
                    newBlogInfo.ArticleLink = new Uri(currentUri, link).ToString();

                    string titleName = Regex.Replace(myMatch.Groups[TitleRegexGroupNum].Value.Replace("\r\n", " ").Trim(), @"( )\1+", "$1", RegexOptions.None);
                    Console.WriteLine(titleName);
                    newBlogInfo.ArticleTitle = titleName;

                    string datetime = myMatch.Groups[DatetimeRegexGroupNum].Value;
                    Console.WriteLine(datetime);

                    //Be careful of the Chinese Datetime Format
                    DateTime blogDatetime;
                    bool DatetimeParseSuccess = DateTime.TryParse(datetime, out blogDatetime);

                    if (!DatetimeParseSuccess)
                    {
                        Regex myChineseDatetimeRegex = ChineseDateTimeRegex;


                        Match myMatch3 = myChineseDatetimeRegex.Match(datetime);
                        if (myMatch3.Success)
                        {
                            string strYear = myMatch3.Groups[ChineseYearRegexGroupNum].Value;
                            string strMonth = myMatch3.Groups[ChineseMonthRegexGroupNum].Value;
                            string strDate = myMatch3.Groups[ChineseDateRegexGroupNum].Value;

                            if (!DateTime.TryParse(strYear + "-" + strMonth + "-" + strDate, out blogDatetime))
                            {
                                blogDatetime = DateTime.Now;
                            }
                        }
                    }

                    newBlogInfo.ArticleDateTime = Convert.ToDateTime(blogDatetime);

                    //If exists LatestPublishDate, break the while loop if newBlogInfo.ArticleDateTime < LatestPublishDate
                    if (LatestPublishDate != null && DateTime.Compare(newBlogInfo.ArticleDateTime, LatestPublishDate) < 0)
                    {
                        needUpadteFlag = false;
                        break;
                    }

                    //Add New BlogInfo to NewBlogInfoList if it doesn't exist in PreviousBlogInfoList
                    if (PreviousBlogInfoList != null && !PreviousBlogInfoList.Contains(newBlogInfo))
                    {
                        NewBlogInfoList.Add(newBlogInfo);
                    }
                    else
                    {
                        needUpadteFlag = false;
                        break;
                        //Console.WriteLine("Reduplicate BlogInfo, Need to break the loop.");
                    }

                    myMatch = myMatch.NextMatch();
                }
            }

            Match myMatch2 = NextPageRegex.Match(inputString);
            if (myMatch2.Success && needUpadteFlag)
            {
                string nextPageLink = myMatch2.Groups[NextPageRegexGroupNum].Value;
                result = new Uri(currentUri, nextPageLink);
            }
            else
            {
                result = null;
            }

            return result;
        }

        /// <summary>
        /// Send Latest BlogInfo By Email
        /// </summary>
        public void SendLatestBlogInfoByEmail()
        {
            StringBuilder emailHTMLContent = new StringBuilder();

            if (NewBlogInfoList != null && NewBlogInfoList.Count > 0)
            {
                //Console.WriteLine("There exists New BlogInfo List!");

                emailHTMLContent.Append("<ul>");

                foreach (BlogInfo blogInfo in NewBlogInfoList)
                {
                    //Console.WriteLine(blogInfo.ArticleTitle + blogInfo.ArticleLink + blogInfo.ArticleDateTime.ToShortDateString());
                    emailHTMLContent.Append("<li>" + "<a href=\"" + blogInfo.ArticleLink + "\">" + blogInfo.ArticleTitle + "</a>" + "&nbsp;&nbsp;" + blogInfo.ArticleDateTime.ToString() + "</li>");
                }

                emailHTMLContent.Append("</ul>");

                MailUtil.SendSmtpHTMLMail(MailUtil.DefaultEmailAddressFrom, MailUtil.EmailReceiverList, InputBlogName + "'s Latest Blog Articles", emailHTMLContent.ToString());
            }
        }

        /// <summary>
        /// Combine And Save BlogInfo List to XML File.
        /// </summary>
        public void CombineAndSaveBlogInfoList()
        {
            List<BlogInfo> combinedBlogInfoList = PreviousBlogInfoList;
            combinedBlogInfoList.AddRange(NewBlogInfoList);
            combinedBlogInfoList.Sort(new DatetimeDESCComparision());

            FileStream fs = new FileStream(XMLFilePath, FileMode.Create);
            TextWriter writer = new StreamWriter(fs, Encoding.GetEncoding(EncodingCharset));
            writer.WriteLine(SerializerManager.Serialize<List<BlogInfo>>(combinedBlogInfoList));
            writer.Close();
        }


        /// <summary>
        /// Execute tasks in order
        /// </summary>
        public void ExecuteTask()
        {
            //Task1: Output Blog Name
            Console.WriteLine("Scanning {0}'s Blog....", InputBlogName);


            //Task2: Create Directory of OutPath
            PathManager.GenerateDirectory(OutPath);
            //Console.WriteLine("Create Directory of OutPath....");


            //Task3:Get Previous BlogInfo List and sort it if exists
            GetAndSortPreviousBlogInfoList();


            //Task4:Get the lastest publish date
            GetLastestPublishDatetime();
            //Console.WriteLine("Lastest Publish Datetime:{0}", LatestPublishDate.ToString());


            //Task5:Get All new BlogInfo List
            GetAllNewBlogInfoList();


            //Task6:Sending the new BlogInfo List by Email
            SendLatestBlogInfoByEmail();


            //Task7: Combine the PreviousBlogInfoList and NewBlogInfoList, then save to XML files.
            CombineAndSaveBlogInfoList();

        }
    }
}
